package FSMDE.controllers.tests;


import javax.xml.parsers.ParserConfigurationException;

import main.java.FSMDE.controllers.DiagramController;
import main.java.FSMDE.controllers.factories.test.mock.MockInterfaceControllerFactory;
import main.java.FSMDE.controllers.factories.test.mock.MockTransitionControllerFactory;
import main.java.FSMDE.models.Diagram;
import main.java.FSMDE.models.Interface;
import main.java.FSMDE.models.Transition;
import main.java.FSMDE.models.TransitionType;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import ERMDE.controllers.tests.mocks.MockFileSystemService;
import ERMDE.controllers.tests.mocks.MockXmlFileManager;
import ERMDE.persistence.tests.TestUtilities;
import FSMDE.controllers.tests.mocks.MockDiagramListener;
import FSMDE.controllers.tests.mocks.MockDiagramView;
import FSMDE.controllers.tests.mocks.MockDiagramXmlManager;
import FSMDE.controllers.tests.mocks.MockGraphPersistenceService;
import FSMDE.controllers.tests.mocks.MockInterfaceController;
import FSMDE.controllers.tests.mocks.MockProjectContext;
import FSMDE.controllers.tests.mocks.MockProjectValidationService;
import FSMDE.controllers.tests.mocks.MockTransitionController;

public class DiagramControllerTestCase {

	private MockProjectContext projectContext;
	private MockDiagramView diagramView;
	private MockInterfaceController interfaceController;
	private MockInterfaceControllerFactory interfaceControllerFactory;
	private MockTransitionController transitionController;
	private MockTransitionControllerFactory transitionControllerFactory;
	private MockXmlFileManager xmlFileManager;
	private MockDiagramXmlManager diagramXmlManager;
	private MockGraphPersistenceService graphPersistenceService;
	private MockProjectValidationService projectValidationService;
	private MockFileSystemService fileSystemService;

	@Before
	public void setUp() throws Exception {
		this.projectContext = new MockProjectContext();
		this.diagramView = new MockDiagramView();
		this.interfaceController = new MockInterfaceController();
		this.interfaceControllerFactory = new MockInterfaceControllerFactory();
		this.interfaceControllerFactory.setController(this.interfaceController);
		this.transitionController = new MockTransitionController();
		this.transitionControllerFactory = new MockTransitionControllerFactory();
		this.transitionControllerFactory
				.setController(this.transitionController);
		this.xmlFileManager = new MockXmlFileManager();
		this.diagramXmlManager = new MockDiagramXmlManager();
		this.graphPersistenceService = new MockGraphPersistenceService();
		this.projectValidationService = new MockProjectValidationService();
		this.fileSystemService = new MockFileSystemService();
		this.fileSystemService.setExistsReturnValue(true);
	}

	@Test
	public void testShouldSetControllerToView() {

		DiagramController diagramController = createController();

		Assert.assertSame(diagramController, this.diagramView.getController());
		Assert.assertSame(diagramController.getGraph(),
				this.diagramView.getGraph());
	}

	@Test
	public void testShouldCreateInterfaceThroughInterfaceControllerWhenCreatingInterfaceWithoutPosition() {

		Interface interface0 = new Interface("Interface");

		DiagramController diagramController = createController();

		Assert.assertEquals(0, this.interfaceController.getCreateCallsCount());
		Assert.assertFalse(diagramController.hasPendingInterface());

		diagramController.createInterface();
		diagramController.handleCreatedEvent(interface0);

		Assert.assertEquals(1, this.interfaceController.getCreateCallsCount());
		Assert.assertTrue(diagramController.hasPendingInterface());

	}

	@Test
	public void testShouldNotCreateInterfaceIfThereIsAPendingInterface() {

		Interface interface0 = new Interface("Interface");

		DiagramController diagramController = this.createController();

		Assert.assertEquals(0, this.interfaceController.getCreateCallsCount());
		Assert.assertFalse(diagramController.hasPendingInterface());

		diagramController.createInterface();
		diagramController.handleCreatedEvent(interface0);

		Assert.assertEquals(1, this.interfaceController.getCreateCallsCount());
		Assert.assertTrue(diagramController.hasPendingInterface());

		diagramController.createInterface();

		Assert.assertEquals(1, this.interfaceController.getCreateCallsCount());
		Assert.assertTrue(diagramController.hasPendingInterface());
	}

	@Test
	public void testShouldCreateTransitionThroughTransitionControllerWhenCreateTransitionIsCalled() {

		DiagramController controller = this.createController();

		Assert.assertFalse(this.transitionController.createWasCalled());

		controller.createTransition();

		Assert.assertTrue(this.transitionController.createWasCalled());
	}

	@Test
	public void testShouldRegisterToHandleTransitionCreatedEvents() {

		DiagramController controller = this.createController();

		Assert.assertEquals(0, this.transitionController.getListeners().size());

		controller.createTransition();

		Assert.assertEquals(1, this.transitionController.getListeners().size());
		Assert.assertSame(controller, this.transitionController.getListeners()
				.toArray()[0]);
	}

	@Test
	public void testShouldRegisterToHandleInterfaceCreatedEvents() {

		DiagramController diagramController = this.createController();
		Assert.assertEquals(0, this.interfaceController.getListeners().size());

		diagramController.createInterface();

		Assert.assertEquals(1, this.interfaceController.getListeners().size());
		Assert.assertSame(diagramController, this.interfaceController
				.getListeners().toArray()[0]);
	}

	@Test
	public void testShouldCallSaveWithDiagramNameDashCompWhenSaveIsCalled()
			throws ParserConfigurationException {

		this.projectContext.setName("projectName");

		Document document = TestUtilities.createDocument();
		this.xmlFileManager.setDocumentToCreate(document);
		this.diagramXmlManager.setElementNameOfRoot("diagram");

		DiagramController controller = this.createController();
		controller.getDiagram().setName("Diagram");

		Assert.assertNull(this.xmlFileManager.getDocumentToSave());
		Assert.assertNull(this.xmlFileManager.getPathToSave());
		Assert.assertFalse(this.xmlFileManager.wasCreateDocumentCalled());
		Assert.assertEquals(0, this.graphPersistenceService.getSaveCalls());
		Assert.assertNull(this.graphPersistenceService.getGraphToSave());

		controller.save();

		Assert.assertTrue(this.xmlFileManager.wasCreateDocumentCalled());
		Assert.assertNotNull(this.xmlFileManager.getDocumentToSave());
		Assert.assertNotNull(this.xmlFileManager.getPathToSave());

		Assert.assertEquals(1, this.graphPersistenceService.getSaveCalls());
		Assert.assertSame(controller.getGraph(),
				this.graphPersistenceService.getGraphToSave());
		Assert.assertEquals("projectName/Datos/Diagram-rep",
				this.graphPersistenceService.getSavePath());

		Assert.assertSame(controller.getDiagram(),
				this.diagramXmlManager.getDiagramRelatedToElement());
		Assert.assertSame(document, this.xmlFileManager.getDocumentToSave());
		Assert.assertEquals("diagram",
				((Element) document.getFirstChild()).getTagName());
		Assert.assertEquals("projectName/Datos/Diagram-comp",
				this.xmlFileManager.getPathToSave());
	}

	@Test
	public void testShouldOpenADiagram() throws Exception {

		String path = "diagrama.xml";
		DiagramController controller = this.createController();

		controller.openDiagram(path);

		Assert.assertEquals(path, this.xmlFileManager.getPathsRead().get(0));
		Assert.assertSame(this.xmlFileManager.getCreatedDocuments().get(0)
				.getDocumentElement(), this.diagramXmlManager
				.getElementsPassedAsParameter().get(0));
		Assert.assertSame(this.diagramXmlManager.getDiagramRelatedToElement(),
				controller.getDiagram());
	}

	@Test
	public void shouldRaiseInterfaceAddedEventWhenInterfaceIsAdded()
			throws Exception {

		Interface interface0 = new Interface("Product");

		MockDiagramListener listener = new MockDiagramListener();

		DiagramController diagramController = this.createController();
		diagramController.addListener(listener);

		diagramController.createInterface();
		diagramController.handleCreatedEvent(interface0);

		Assert.assertNull(listener.getDiagram());
		Assert.assertNull(listener.getInterface());

		diagramController.addInterface(0, 0);

		Assert.assertSame(interface0, listener.getInterface());
		Assert.assertSame(diagramController.getDiagram(), listener.getDiagram());
	}

	@Test
	public void shouldRaiseSubdiagramCreatedWhenSubdiagramIsCreated()
			throws Exception {
		Document document = TestUtilities.createDocument();
		this.xmlFileManager.setDocumentToCreate(document);
		this.diagramXmlManager.setElementNameOfRoot("diagram");

		MockDiagramListener listener = new MockDiagramListener();

		DiagramController diagramController = this.createController();
		diagramController.getDiagram().setName("Diagram");
		diagramController.addListener(listener);

		Assert.assertNull(listener.getDiagram());
		Assert.assertNull(listener.getDiagramName());

		diagramController.createSubDiagram("ChildDiagram", "InterfaceName");

		Assert.assertSame(diagramController.getDiagram(), listener.getDiagram());
		Assert.assertEquals("ChildDiagram", listener.getDiagramName());
	}

	@Test
	public void shouldSaveDiagramWhenCreatingSubDiagram() throws Exception {

		this.projectContext.setName("projectName");

		Document document = TestUtilities.createDocument();
		this.xmlFileManager.setDocumentToCreate(document);
		this.diagramXmlManager.setElementNameOfRoot("diagram");

		DiagramController diagramController = this.createController();
		diagramController.getDiagram().setName("Diagram");

		Assert.assertNull(this.xmlFileManager.getDocumentToSave());
		Assert.assertNull(this.xmlFileManager.getPathToSave());
		Assert.assertFalse(this.xmlFileManager.wasCreateDocumentCalled());
		Assert.assertEquals(0, this.graphPersistenceService.getSaveCalls());
		Assert.assertNull(this.graphPersistenceService.getGraphToSave());

		diagramController.createSubDiagram("ChildDiagram", "InterfaceName");

		Assert.assertTrue(this.xmlFileManager.wasCreateDocumentCalled());
		Assert.assertNotNull(this.xmlFileManager.getDocumentToSave());
		Assert.assertNotNull(this.xmlFileManager.getPathToSave());
		Assert.assertEquals(1, this.graphPersistenceService.getSaveCalls());
		Assert.assertSame(diagramController.getGraph(),
				this.graphPersistenceService.getGraphToSave());
		Assert.assertEquals("projectName/Datos/Diagram-rep",
				this.graphPersistenceService.getSavePath());

		Assert.assertSame(diagramController.getDiagram(),
				this.diagramXmlManager.getDiagramRelatedToElement());
		Assert.assertSame(document, this.xmlFileManager.getDocumentToSave());
		Assert.assertEquals("diagram",
				((Element) document.getFirstChild()).getTagName());
		Assert.assertEquals("projectName/Datos/Diagram-comp",
				this.xmlFileManager.getPathToSave());
	}

	@Test
	public void shouldRaiseTransitionAddedEventWhenTransitionIsAdded()
			throws Exception {

		Interface interface1 = new Interface("Interface1");
		Interface interface2 = new Interface("Interface2");

		DiagramController diagramController = this.createController();

		this.addInterfaceToDiagram(diagramController, interface1, 20, 30);
		this.addInterfaceToDiagram(diagramController, interface2, 60, 30);

		Transition transition = Transition.create("Transition",
				TransitionType.Control);
		transition.setSource(interface1);
		transition.setDestination(interface2);

		MockDiagramListener listener = new MockDiagramListener();

		diagramController.addListener(listener);

		Assert.assertNull(listener.getDiagram());
		Assert.assertNull(listener.getTransition());

		diagramController.createTransition();
		diagramController.handleCreatedEvent(transition);

		Assert.assertSame(transition, listener.getTransition());
		Assert.assertSame(diagramController.getDiagram(), listener.getDiagram());
	}

	@Test
	public void testShouldCallToLoadWithCorrectDiagramName() {

		Diagram diagram = new Diagram();
		diagram.setName("diagram1");

		this.graphPersistenceService.setCellsToLoad(new String[] {});

		DiagramController controller = this.createController();

		controller.load(diagram);

		Assert.assertEquals(this.projectContext.getDataDirectory() + "/"
				+ diagram.getName() + "-rep",
				this.graphPersistenceService.getDiagramName());
	}

	@Test
	public void testShouldSaveCorrectlyCellsFromMxGraphInDiagramController() {

		Diagram diagram = new Diagram();
		diagram.setName("diagram1");

		// TODO hacen falta los conectores de las transisiones
		this.graphPersistenceService.setCellsToLoad(new String[] {
				DiagramController.CellConstants.InterfacePrefix + "1",
				DiagramController.CellConstants.TransitionPrefix + "1", });

		DiagramController controller = this.createController();

		controller.load(diagram);
		Assert.assertEquals(
				2,
				controller.getGraph().getChildCells(
						controller.getGraph().getDefaultParent()).length);

		Assert.assertNotNull(controller.getInterfaceCell("1"));
		Assert.assertNotNull(controller.getTransitionCell("1"));
	}

	/*
	 * @Test public void
	 * testShouldRemoveTransitionConnectorsAndTransitionCellsWhenUpdatingTransition
	 * () { }
	 */

	@Test
	public void testShouldUpdateEntityThroughEntityControllerWhenUpdatingEntity() {

		Interface interface0 = new Interface("Interface");
		DiagramController diagramController = this.createController();

		Assert.assertEquals(0, this.interfaceController.getUpdateCallsCount());
		Assert.assertEquals(0,
				this.interfaceControllerFactory.getCreateCallsCount());

		diagramController.updateInterface(interface0);

		Assert.assertEquals(1,
				this.interfaceControllerFactory.getCreateCallsCount());
		Assert.assertEquals(1, this.interfaceController.getUpdateCallsCount());
	}

	@Test
	public void testShouldUpdateRelationshipThroughRelationshipControllerWhenUpdatingRelationship() {
		Transition transition = Transition.create("transition",
				TransitionType.Control);
		DiagramController diagramController = this.createController();

		Assert.assertEquals(0, this.transitionController.getUpdateCallsCount());
		Assert.assertEquals(0,
				this.transitionControllerFactory.getCreateCallsCount());

		diagramController.updateTransition(transition);

		Assert.assertEquals(1,
				this.transitionControllerFactory.getCreateCallsCount());
		Assert.assertEquals(1, this.transitionController.getUpdateCallsCount());
	}

	private void addInterfaceToDiagram(DiagramController diagramController,
			Interface interface1, int x, int y) throws Exception {
		diagramController.createInterface();
		diagramController.handleCreatedEvent(interface1);
		diagramController.addInterface(x, y);
	}

	private DiagramController createController() {
		return new DiagramController(this.projectContext, this.diagramView,
				this.interfaceControllerFactory,
				this.transitionControllerFactory, this.xmlFileManager,
				this.diagramXmlManager, this.graphPersistenceService,
				this.projectValidationService, this.fileSystemService);
	}

}
